/* 
 * Copyright (C) 2012 UTSChedule
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package utsc.apps.utschedule.classes;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;

import utsc.apps.utschedule.util.TimetableHelper;

import android.content.Intent;
import android.graphics.Color;
import android.text.format.Time;

import com.j256.ormlite.dao.CloseableIterator;
import com.j256.ormlite.dao.ForeignCollection;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.ForeignCollectionField;
import com.j256.ormlite.table.DatabaseTable;

/**
 * This class represents as a data model of the course table.  
 * @author 
 *
 */
@DatabaseTable
public class Course extends GenericEvent implements Comparable<Course> {

	public static final String TIMETABLE="_timetable_id";
	public static final String TUTORIAL="_tutorial";
	public static final String COURSE_CODE="_courseCode";
	public static final String DESCRIPTION="_description";
	public static final String COLOR="_color";
	public static final String INSTRUCTOR="_instructor";
	public static final String SECTION="_section";
	public static final String DAY="_day";
	public static final String SHORTNAME="_shortName";
	public static final String ODDWEEKS="_oddWeeks";
	public static final String COURSE_ID="_courseID";
	public static final String SYNC_ID="_syncID";
	public static final String WEIGHT="_weight";
	public static final String PARENT_COURSE_ID="_parentCourse_id";
	public static final String GOAL_GPA="_goalGpa";
	
	@DatabaseField
	private String _description;
	@DatabaseField
	private int _color=Color.WHITE;
	@DatabaseField
	private String _instructor;
	@DatabaseField(generatedId=true)
	private int _courseID;
	@DatabaseField(foreign=true,foreignAutoCreate=true,canBeNull=false)
	private Timetable _timetable;
	@DatabaseField
	private String _courseCode;
	@DatabaseField
	private String _section;
	@DatabaseField
	private int _day;
	@DatabaseField
	private Date _lastUpdated;
	@DatabaseField
	private String _shortName;
	@DatabaseField
	private boolean _tutorial;
	@DatabaseField
	private int _oddWeeks=1;
	@DatabaseField
	private boolean _overlapped;
	@DatabaseField
	private String _syncID;
	@DatabaseField
	private double _weight=0.5;
	@DatabaseField
	private double _goalGpa=2.5;
	/**
	 * Holds the parent course information. A course itself can be self-referenced.
	 */
	@DatabaseField(foreign=true, foreignAutoCreate=false, canBeNull=false)
	private Course _parentCourse;
	/**
	 * This collection object represents the 1-many relationship with {@link Assignment}.
	 */
	@ForeignCollectionField(eager=false,columnName="assignments")
	private ForeignCollection<Assignment> _assignments;
	/**
	 * The actual list object that holds the list of {@link Assignment assignments}.
	 */
	private ArrayList<Assignment> _assignmentList;
	/**
	 * The list of {@link GenericTask tasks}.
	 */
	private ArrayList<GenericTask> _taskList;
	private int _dueCount=-1;
	private UofTGrade _grade;
	
	public Course(int courseId){
		this._courseID=courseId;
	}
	
	public Course(){
	}
	
	/**
	 * An overloaded constructor that takes an {@link Intent intent} object and initialises the object with all the extra data within the {@link Intent intent}.
	 * @param intent
	 */
	public Course(Intent intent){
		super(intent);
		_description=intent.getStringExtra(DESCRIPTION);
		_color=intent.getIntExtra(COLOR, Color.WHITE);
		_instructor=intent.getStringExtra(INSTRUCTOR);
		_courseCode=intent.getStringExtra(COURSE_CODE);
		_section=intent.getStringExtra(SECTION);
		_day=intent.getIntExtra(DAY,1);
		_shortName=intent.getStringExtra(SHORTNAME);
		_tutorial=intent.getBooleanExtra(TUTORIAL, false);
		_oddWeeks=intent.getIntExtra(ODDWEEKS, 1);
		_syncID=intent.getStringExtra(SYNC_ID);
		_weight=intent.getDoubleExtra(WEIGHT, 0.5);
	}
	
	/**
	 * A helper method to return an {@link Inent intent} object.
	 * @return An {@link Intent intent} object which holds all the data fields. 
	 */
	public Intent getCourseIntent(){
		Intent intent = new Intent();
		intent.putExtra("CourseFormResult", _courseID);
		intent.putExtra(COURSE_ID, _courseID);
		intent.putExtra(Course.COLOR, _color);
		if (_timetable!=null) {
			intent.putExtra(Course.TIMETABLE, _timetable.get_timetableID());
		}else {
			intent.putExtra(Course.TIMETABLE, 0);
		}
		
		intent.putExtra(Course.COURSE_CODE, _courseCode);
		intent.putExtra(Course.DAY, _day);
		intent.putExtra(Course.DESCRIPTION, _description);
		intent.putExtra(Course.INSTRUCTOR, _instructor);
		intent.putExtra(Course.ODDWEEKS, _oddWeeks);
		intent.putExtra(Course.SECTION, _section);
		intent.putExtra(Course.SHORTNAME, _shortName);
		intent.putExtra(Course.TUTORIAL, _tutorial);
		intent.putExtra(Course.STARTTIME, get_startTime());
		intent.putExtra(Course.ENDTIME, get_endTime());
		intent.putExtra(Course.TITLE, get_title());
		intent.putExtra(Course.LOCATION, get_location());
		intent.putExtra(Course.SYNC_ID, _syncID);
		intent.putExtra(WEIGHT, _weight);
		return intent;
	}
	
	public String get_description() {
		return _description;
	}

	public void set_description(String _description) {
		this._description = _description;
	}

	public int get_color() {
		return _color;
	}

	public void set_color(int _color) {
		this._color = _color;
	}

	public String get_instructor() {
		return _instructor;
	}

	public void set_instructor(String _instructor) {
		this._instructor = _instructor;
	}

	public int get_courseID() {
		return _courseID;
	}

	public void set_courseID(int _courseID) {
		this._courseID = _courseID;
	}

	public Timetable get_timetable() {
		return _timetable;
	}
	
	public void set_timetable(Timetable _timetable) {
		this._timetable = _timetable;
	}

	public String get_courseCode() {
		return _courseCode;
	}

	public void set_courseCode(String _courseCode) {
		this._courseCode = _courseCode;
	}

	public String get_section() {
		return _section;
	}

	public void set_section(String _section) {
		this._section = _section;
	}

	public int get_day() {
		return _day;
	}

	public void set_day(int _day) {
		this._day = _day;
	}

	public Date get_lastUpdated() {
		return _lastUpdated;
	}

	public void set_lastUpdated(Date _lastUpdated) {
		this._lastUpdated = _lastUpdated;
	}

	public String get_shortName() {
		return _shortName;
	}

	public void set_shortName(String _shortName) {
		this._shortName = _shortName;
	}

	public boolean is_tutorial() {
		return _tutorial;
	}

	public void set_tutorial(boolean _tutorial) {
		this._tutorial = _tutorial;
	}

	public int get_oddWeeks() {
		return _oddWeeks;
	}

	public void set_oddWeeks(int _oddWeeks) {
		this._oddWeeks = _oddWeeks;
	}

	public boolean is_overlapped() {
		return _overlapped;
	}

	public void set_overlapped(boolean _overlapped) {
		this._overlapped = _overlapped;
	}

	@Override
	public String toString() {
		if (_shortName!=null&&_shortName.trim().length()>0) {
			return _shortName;
		}
		return _courseCode;
	}

	/**
	 * Implement the comparator interface to compare two different courses by their GPAs.
	 */
	@Override
	public int compareTo(Course another) {
		int result=0;
		/*int day1=_day;
		int day2=another.get_day();
		long starttime1=get_startTime();
		long starttime2=another.get_startTime();
		long endtime1=get_endTime();
		long endtime2=another.get_endTime();*/
		double gpa1=get_grade().get_GPA();
		double gpa2=another.get_grade().get_GPA();
		if (another!=null) {
			if (gpa1<gpa2) {
				result=-1;
			}
			else if (gpa1==gpa2) {
				result=0;
			}else {
				result=1;
			}
		}
		return result;
	}

	/**
	 * Get the cached list of assignments.
	 * @return
	 */
	public ArrayList<Assignment> get_assignmentList() {
		if (_assignmentList==null ) {
			//Log.d("course", "get assignments");
			refreshAssignmentList();
		}
		return _assignmentList;
	}
	
	/**
	 * This method will return a list of non-expired assignments from the cached list.
	 * @return
	 */
	public ArrayList<Assignment> get_unexpiredAssignmentList(){
		if (get_assignmentsDueCount()>0) {
			ArrayList<Assignment> assignments=new ArrayList<Assignment>();
			for (Assignment assignment : _assignmentList) {
				if (!assignment.is_expired()) {
					assignments.add(assignment);
				}
			}
			return assignments;
		}
		return null;
	}
	
	/**
	 * Returns the count of assignments that are not finished.
	 * @return
	 */
	public int get_assignmentsDueCount(){
		//if (_dueCount<0) {
			get_assignmentList();
			_dueCount=_assignmentList.size();
			Time now=new Time();
			now.setToNow();
			now.second=0;
			now.normalize(false);
			for (Assignment assignment : _assignmentList) {
				Time t=new Time();
				t.set(TimetableHelper.convertFromDateTimeStringToMillis(assignment.get_DueDate()+" "+assignment.get_DueTime(), TimetableHelper.TYPE_DATETIME).getTime());
				
				if (!t.after(now)) {
					assignment.set_expired(true);
					_dueCount--;
				}else {
					assignment.set_expired(false);
				}
			}
		//}
		return _dueCount;
	}
	
	public int get_courseAverage(){
		//get_assignmentList();
		Double average = 0.0;
		if (this._taskList!=null && !this._taskList.isEmpty()) {
			double marks = 0;
			double total = 0;
			double weight;
			for (GenericTask task: this._taskList) {
				weight = task.get_weight() / 100;
				marks += task.get_mark() * weight;
				total += task.get_total() * weight;
			}
			
			average = (marks / total );
		}
		return average.intValue();
	}

	public void set_assignmentList(ArrayList<Assignment> _assignmentList) {
		this._assignmentList = _assignmentList;
	}
	
	public void addAssignment(Assignment assignment) {
		_assignments.add(assignment);
	}
	
	/**
	 * This will refresh the cached assignment list.
	 */
	public void refreshAssignmentList() {
		//Log.d("course", "refersh assignments");
		try {
			if (_assignments!=null) {
				CloseableIterator<Assignment> iterator=_assignments.closeableIterator();
				if (_assignmentList==null) {
					_assignmentList=new ArrayList<Assignment>();
				}else {
					_assignmentList.clear();
				}
				try {
					
					while (iterator.hasNext()) {
						final Assignment a=iterator.next();
						a.refresh_grade();
						_assignmentList.add(a);
					}
					_dueCount=-1;
				} finally{
					iterator.close();
				}
			}else {
				if (_assignmentList==null) {
					_assignmentList=new ArrayList<Assignment>();
				}else {
					_assignmentList.clear();
				}
			}
			
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Returns back the sync ID of this {@link Course course}. An sync ID is created when the this {@link Course course} 
	 * is coming from the UTSC intranet.
	 * @return
	 */
	public String get_syncID() {
		return _syncID;
	}

	public void set_syncID(String _syncID) {
		this._syncID = _syncID;
	}

	public ArrayList<GenericTask> get_taskList() {
		return _taskList;
	}

	/**
	 * Sets the {@link GenericTask task list}. It also refresh the {@link UofTGrade course grade} as well.
	 * @param _taskList
	 */
	public void set_taskList(ArrayList<GenericTask> _taskList) {
		this._taskList = _taskList;
		setGrade();
	}

	/**
	 * Calculates the {@link UofTGrade course grade} by iterating through the {@link GenericTask task list}.
	 */
	private void setGrade() {
			double marks = 0;
			double total = 0;
			if (this._taskList!=null && !this._taskList.isEmpty()) {
				double weight;
				double taskTotal;
				for (GenericTask task: this._taskList) {
					weight = task.get_weight() ;
					taskTotal=task.get_total();
					if (weight>0 && taskTotal>0) {
						marks += task.get_mark()/task.get_total()*weight;
						total += weight;
					}
				}
			}
			if (total==0) {
				total=1;
			}
			_grade=new UofTGrade(marks, total, _weight);
	}
	
	public UofTGrade get_grade() {
		return _grade;
	}

	public double get_weight() {
		return _weight;
	}

	public void set_weight(double _weight) {
		this._weight = _weight;
	}
	
	public void set_parentCourse(Course _parentCourse) {
		this._parentCourse = _parentCourse;
	}
	
	public Course get_parentCourse() {
		return _parentCourse;
	}
	public double get_goalGpa() {
		return _goalGpa;
	}
	public void set_goalGpa(double _goalGpa) {
		this._goalGpa = _goalGpa;
	}
	
}
